package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	fmt.Println("16. 通道 (Channels)")

	// 什么是通道
	fmt.Println("\n--- 什么是通道 ---")

	// 创建通道
	var ch1 chan int         // 声明一个通道，但未初始化（nil通道）
	ch2 := make(chan int)    // 创建无缓冲通道
	ch3 := make(chan int, 3) // 创建带缓冲的通道，容量为3

	fmt.Printf("ch1: %v (nil通道)\n", ch1)
	fmt.Printf("ch2: %v (无缓冲通道)\n", ch2)
	fmt.Printf("ch3: %v (带缓冲通道，容量%d)\n", ch3, cap(ch3))

	// 无缓冲通道的基本使用
	fmt.Println("\n--- 无缓冲通道的基本使用 ---")

	// 启动一个协程发送数据
	go func() {
		fmt.Println("协程: 准备发送数据 42")
		ch2 <- 42 // 发送数据到通道
		fmt.Println("协程: 数据发送完成")
	}()

	// 主协程接收数据
	fmt.Println("主协程: 准备接收数据")
	value := <-ch2 // 从通道接收数据
	fmt.Printf("主协程: 接收到数据 %d\n", value)

	// 带缓冲通道的使用
	fmt.Println("\n--- 带缓冲通道的使用 ---")

	// 发送数据到带缓冲的通道
	fmt.Printf("发送前通道长度: %d, 容量: %d\n", len(ch3), cap(ch3))
	ch3 <- 1
	ch3 <- 2
	ch3 <- 3
	fmt.Printf("发送3个数据后通道长度: %d, 容量: %d\n", len(ch3), cap(ch3))

	// 接收数据
	val1 := <-ch3
	val2 := <-ch3
	val3 := <-ch3
	fmt.Printf("接收到的数据: %d, %d, %d\n", val1, val2, val3)
	fmt.Printf("接收后通道长度: %d, 容量: %d\n", len(ch3), cap(ch3))

	// 通道的方向
	fmt.Println("\n--- 通道的方向 ---")

	// 创建通道
	ch4 := make(chan string)

	// 启动发送协程
	go sendOnly(ch4) // 只发送的通道

	// 启动接收协程
	go receiveOnly(ch4) // 只接收的通道

	// 等待协程执行完毕
	time.Sleep(100 * time.Millisecond)

	// 通道的选择器 (select)
	fmt.Println("\n--- 通道选择器 (select) ---")

	// 创建多个通道
	ch5 := make(chan string)
	ch6 := make(chan string)

	// 启动协程
	go func() {
		time.Sleep(100 * time.Millisecond)
		ch5 <- "来自通道5的消息"
	}()

	go func() {
		time.Sleep(200 * time.Millisecond)
		ch6 <- "来自通道6的消息"
	}()

	// 使用 select 等待多个通道
	for i := 0; i < 2; i++ {
		select {
		case msg1 := <-ch5:
			fmt.Println("接收到:", msg1)
		case msg2 := <-ch6:
			fmt.Println("接收到:", msg2)
		case <-time.After(500 * time.Millisecond):
			fmt.Println("超时!")
		}
	}

	// 通道的实际应用示例
	fmt.Println("\n--- 通道的实际应用示例 ---")

	// 使用通道进行协程间通信
	numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	resultChan := make(chan int, len(numbers))

	// 启动多个协程处理数据
	var wg sync.WaitGroup
	for _, num := range numbers {
		wg.Add(1)
		go func(n int) {
			defer wg.Done()
			// 模拟处理时间
			time.Sleep(time.Duration(n*100) * time.Millisecond)
			resultChan <- n * n // 发送处理结果
		}(num)
	}

	// 等待所有协程完成
	go func() {
		wg.Wait()
		close(resultChan) // 关闭通道
	}()

	// 接收处理结果
	fmt.Println("处理结果:")
	for result := range resultChan {
		fmt.Printf("接收到结果: %d\n", result)
	}

	// 使用通道实现工作池模式
	fmt.Println("\n--- 工作池模式 ---")
	jobs := make(chan int, 100)
	results := make(chan int, 100)

	// 启动3个工作协程
	for w := 1; w <= 3; w++ {
		go worker(w, jobs, results)
	}

	// 发送5个任务
	for j := 1; j <= 5; j++ {
		jobs <- j
	}
	close(jobs)

	// 收集结果
	for a := 1; a <= 5; a++ {
		result := <-results
		fmt.Printf("任务结果: %d\n", result)
	}

	// 通道的关闭和范围
	fmt.Println("\n--- 通道的关闭和范围 ---")

	// 创建通道并发送数据
	queue := make(chan string, 3)
	queue <- "one"
	queue <- "two"
	queue <- "three"
	close(queue) // 关闭通道

	// 使用 range 遍历通道直到关闭
	for elem := range queue {
		fmt.Printf("接收到: %s\n", elem)
	}

	// 检查通道是否关闭
	fmt.Println("\n--- 检查通道是否关闭 ---")
	ch7 := make(chan int, 1)
	ch7 <- 42
	close(ch7)

	// 接收数据并检查通道是否关闭
	if value, ok := <-ch7; ok {
		fmt.Printf("接收到数据: %d\n", value)
	} else {
		fmt.Println("通道已关闭且无数据")
	}

	// 再次尝试接收
	if value, ok := <-ch7; ok {
		fmt.Printf("接收到数据: %d\n", value)
	} else {
		fmt.Println("通道已关闭且无数据")
	}

	// 使用默认情况避免阻塞
	fmt.Println("\n--- 使用默认情况避免阻塞 ---")
	messages := make(chan string)
	signals := make(chan bool)

	// 非阻塞接收
	select {
	case msg := <-messages:
		fmt.Printf("接收到消息: %s\n", msg)
	default:
		fmt.Println("没有消息接收")
	}

	// 非阻塞发送
	msg := "你好"
	select {
	case messages <- msg:
		fmt.Printf("发送消息: %s\n", msg)
	default:
		fmt.Println("消息未发送")
	}

	// 多路复用器默认情况
	select {
	case msg := <-messages:
		fmt.Printf("接收到消息: %s\n", msg)
	case sig := <-signals:
		fmt.Printf("接收到信号: %t\n", sig)
	default:
		fmt.Println("没有活动")
	}
}

// 只发送的通道函数
func sendOnly(ch chan<- string) {
	ch <- "只发送数据"
}

// 只接收的通道函数
func receiveOnly(ch <-chan string) {
	msg := <-ch
	fmt.Printf("只接收数据: %s\n", msg)
}

// 工作协程
func worker(id int, jobs <-chan int, results chan<- int) {
	for j := range jobs {
		fmt.Printf("工作协程 %d 正在处理任务 %d\n", id, j)
		time.Sleep(time.Second)
		results <- j * 2
	}
}
